package org.doubango.utils

import android.annotation.TargetApi
import android.content.ContextWrapper
import android.content.Intent
import android.database.Cursor
import android.os.Build
import android.os.Process
import android.os.UserHandle
import android.text.format.DateFormat
import com.evendai.loglibrary.Timber
import org.doubango.ngn.NgnApplication
import java.net.Inet6Address
import java.net.InetAddress
import java.net.NetworkInterface
import java.util.*
import java.util.regex.Pattern


@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
object Util {

    val TAG = Util::class.java.simpleName

    /** 把byte转换为十六进制的表现形式，元素之间用逗号分隔，如：0xff, 0xa4 0x42  */
    fun byteToHex(bytes: ByteArray): String {
        val sb = StringBuilder()
        for (i in bytes.indices) {
            val hex = byteToHex(bytes[i])
            sb.append(hex).append(", ")
        }

        // 删除最后面的逗号与空格
        sb.deleteCharAt(sb.length - 1)
        sb.deleteCharAt(sb.length - 1)
        return sb.toString()
    }

    /** 把字byte转换为十六进制的表现形式，如0xff  */
    fun byteToHex(aByte: Byte): String {
        // 0x原样输出，%开始为特殊含义，2表示总长度为两位，0表示长度不够时用0补齐，x表示以十六进制表示
        return String.format("0x%02x", aByte.toInt() and 0xFF)
    }

    /** 获取指定Class的所有变量  */
    fun getAllStaticFieldsName(clazz: Class<*>): List<String> {
        val fields = clazz.fields
        val names = ArrayList<String>()
        for (field in fields) {
            names.add(field.name)
        }
        return names
    }

    fun getStaticFieldIntValue(clazz: Class<*>, fieldName: String): Int {
        val intValue = clazz.getField(fieldName).getInt(null)
//        Timber.i("$fieldName = $intValue")
        return intValue
    }

    fun getStaticFieldStringValue(clazz: Class<*>, fieldName: String): String {
        val stringValue = clazz.getField(fieldName).get(null) as String
//        Timber.i("$fieldName = $stringValue")
        return stringValue
    }

    /**
     * 获取正在联网的ip地址列，能获取Wifi的与电话卡上网的和插网线的ip，当连上Wifi时，就获取不到手机卡的，Wifi断开时再获取就能获取到手机卡的
     * 网络接口名以rmnet打头的代表的是移动卡的网络(也不一定的，模拟器用的是radio0，公司的手机用的是ccmni0，这个接口名称貌似是手机定好的，不同手机不一样，
     * 定好之后，同一台手机换卡了，接口名称也是不会变的)，以wlan打头的是wifi网络，以eth打头的是有线网络，lo代表的是本机地址（127.0.0.1）
     * @return 返回所有已连接的网络的ip，如果没有则返回空列表
     * 示例地址:
     * 网络接口名称：rmnet_data0, 绑定到此网络接口的地址如下：
     * ip = 172.18.94.108，是ipv6地址吗：false, 是连接本地地址吗：false
     * ip = fe80::8564:ddf5:7c16:1e4e%rmnet_data0，是ipv6地址吗：true, 是连接本地地址吗：true
     * ------------------------------------------------------------------------------------------------------------------
     * 网络接口名称：wlan0, 绑定到此网络接口的地址如下：
     * ip = 192.168.1.51，是ipv6地址吗：false, 是连接本地地址吗：false
     * ip = fe80::a6d4:b2ff:fe02:acab%wlan0，是ipv6地址吗：true, 是连接本地地址吗：true
     * ------------------------------------------------------------------------------------------------------------------
     * 网络接口名称：lo, 绑定到此网络接口的地址如下：
     * ip = 127.0.0.1，是ipv6地址吗：false, 是连接本地地址吗：false
     * ip = ::1，是ipv6地址吗：true, 是连接本地地址吗：false
     * ------------------------------------------------------------------------------------------------------------------
     * 网络接口名称：lo, 绑定到此网络接口的地址如下：
     * ip = 192.168.1.19，是ipv6地址吗：false, 是连接本地地址吗：false
     * ip = fe80::2e0:4cff:fe68:565%eth0，是ipv6地址吗：true, 是连接本地地址吗：true
     */
    fun getLocalIpList() = NetworkInterface.getNetworkInterfaces().asSequence()
            .filter { ni: NetworkInterface -> ni.inetAddresses.toList().isNotEmpty() && !ni.isLoopback  && ni.isUp} // 过滤：必须要有ip地址的，且不能是环回接口（即把数据发送给本机的网络接口：
                                                                                                         // 回送地址，ipv4为127.0.0.1，ipv6为::1）
            .flatMap { ni: NetworkInterface -> ni.inetAddresses.asSequence() }                           // 把每个网络接口中的ip地址列表合成一个大列表
            .filter { ip: InetAddress -> !(ip is Inet6Address && ip.isLinkLocalAddress) }                // 过滤：如果是ipv6，不能是连接本地地址
            .toList()

    fun isIpAddress(str: String): Boolean {
        val pattern = "(2(5[0-5]{1}|[0-4]\\d{1})|[0-1]?\\d{1,2})(\\.(2(5[0-5]{1}|[0-4]\\d{1})|[0-1]?\\d{1,2})){3}"
        val r = Pattern.compile(pattern)
        val m = r.matcher(str)
        return m.matches()
    }

    /**
     * 打印Cursor中所有的记录
     * @param cursor
     */
    fun printCursor(cursor: Cursor?) {
        if (!cursorHasDataAndMoveToFirst(cursor)) {
            Timber.i("读取出来的Cursor中没有数据")
            return
        }

        Timber.i("总共有 " + cursor!!.count + "条记录")
        // 遍历每一行记录
        do {
            Timber.i("------------------------------------------------------------------------------------------------------------------")
            // 遍历一行中的每一列
            for (i in 0 until cursor!!.columnCount) {
                val columnName = cursor!!.getColumnName(i)
                val columnValue = cursor!!.getString(i)
                Timber.i("$columnName = $columnValue")
            }
        } while (cursor.moveToNext())
    }

    /**
     * 判断Cursor是否有数据，有数据则返回true，并且把Cursor移到第一条记录。如果没有数据则返回false。
     */
    fun cursorHasDataAndMoveToFirst(cursor: Cursor?): Boolean {
        return cursor != null && cursor.count > 0 && cursor.moveToFirst()
    }

    /**
     * 获取指定的参数ok结果是否OK
     * @param ok
     * @return 指定的参数ok为true时返回“OK”，否则返回“不OK”
     */
    fun getIsOk(ok: Boolean) = if (ok) "OK" else "不OK"

    /**
     * 把一个毫秒的时长格式化为字符串，如果毫秒有包含小时，则格式化为：时:分:秒，如01:30:49，否则格式化为分:秒，如30:49
     * @param duration
     * @return
     */
    fun formatMillis(duration: Long): CharSequence {
        val calendar = Calendar.getInstance() // 以当前的时间创建一个日历对象
        calendar.clear()   // 清空时间，保留到：（格林尼治标准时间）1970 年 1 月 1 日 00:00:00
        val oneHourMillis = (1 * 60 * 60 * 1000).toLong()    // 1小时对应的毫秒值
        val oneDayMillis = oneHourMillis * 24               // 1天对应的毫秒值
        val days = duration / oneDayMillis;
        val residueMillis = duration - (days * oneDayMillis) // 除去天数剩余的毫秒值
        calendar.add(Calendar.MILLISECOND, residueMillis.toInt()) // 把毫秒值塞到日历对象中

//        val hasHour = residueMillis / oneHourMillis > 0
        val inFormat = /*if (hasHour) */"kk小时mm分"/* else "mm:ss"*/

        if (days > 0) {
            return "运行：$days 天 ${DateFormat.format(inFormat, calendar)}"
        } else {
            return "运行：${DateFormat.format(inFormat, calendar)}"
        }
    }

    private fun executeComponentMethod(context: ContextWrapper, methodName: String, service: Intent) {
        val mBase = context.baseContext
        val method = mBase.javaClass.getMethod(methodName, Intent::class.java, UserHandle::class.java)
        method.invoke(mBase, service, Process.myUserHandle())
    }

    fun startActivityAsUser(intent: Intent) {
        executeComponentMethod(NgnApplication.getInstance(), "startActivityAsUser", intent)
    }

    fun startServiceAsUser(intent: Intent) {
        executeComponentMethod(NgnApplication.getInstance(), "startServiceAsUser", intent)
    }

    fun stopServiceAsUser(intent: Intent) {
        executeComponentMethod(NgnApplication.getInstance(), "stopServiceAsUser", intent)
    }

    fun sendBroadcastAsUser(intent: Intent) {
        NgnApplication.getInstance().sendBroadcastAsUser(intent, Process.myUserHandle());
    }

}
